//
//  LcTimerRepeat.m
//  18Birdies
//
//  Created by weihua on 2017/3/16.
//  Copyright © 2017年 Lc. All rights reserved.
//

#import "LcTimerRepeat.h"

static const uint64_t kTimerLeeway = 0.1 * NSEC_PER_SEC; // 1 second

@interface LcTimerRepeat ()

@property(nonatomic, strong) dispatch_queue_t internalQueue;
@property(nonatomic, strong) dispatch_source_t gcdTimer;
@property(nonatomic, assign) BOOL suspended;
@property(nonatomic, copy) TimerRepeatBlock block;
@property(nonatomic, assign) NSTimeInterval startTime;
@property(nonatomic, assign) NSTimeInterval intervalTime;

@end

@implementation LcTimerRepeat

+ (dispatch_queue_t)uniqueQueue {
  NSString* queueLabel = [NSString stringWithFormat:@"com.timer.%@", [NSUUID UUID].UUIDString];
  return dispatch_queue_create(queueLabel.UTF8String, NULL);
}

+ (instancetype)timerWithInterval:(NSTimeInterval)intervalTime block:(TimerRepeatBlock)block {
  return [self timerWithInterval:intervalTime startTime:0 block:block];
}

+ (instancetype)timerWithInterval:(NSTimeInterval)intervalTime
                        startTime:(NSTimeInterval)startTime
                            block:(TimerRepeatBlock)block {
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  return [self timerWithInterval:intervalTime startTime:startTime block:block queue:queue];
}

+ (instancetype)timerWithInterval:(NSTimeInterval)intervalTime
                        startTime:(NSTimeInterval)startTime
                            block:(TimerRepeatBlock)block
                            queue:(dispatch_queue_t)queue {
  return
      [[self alloc] initWithWithInterval:intervalTime startTime:startTime block:block queue:queue];
}

- (instancetype)initWithWithInterval:(NSTimeInterval)intervalTime
                           startTime:(NSTimeInterval)startTime
                               block:(TimerRepeatBlock)block
                               queue:(dispatch_queue_t)queue {
  self = [super init];
  if (self) {
    _internalQueue = queue;
    _block = [block copy];
    _suspended = YES;
    _startTime = startTime;
    _intervalTime = intervalTime;
  }
  return self;
}

- (BOOL)running {
  return self.gcdTimer != nil;
}

- (void)start {
  if (!self.running) {
    self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.internalQueue);
    dispatch_source_set_timer(self.gcdTimer,
                              dispatch_time(DISPATCH_TIME_NOW, self.startTime * NSEC_PER_SEC),
                              self.intervalTime * NSEC_PER_SEC, kTimerLeeway);
    self.startTime = 0;

    __weak typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(self.gcdTimer, ^{
      dispatch_async(dispatch_get_main_queue(), ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf.block != nil) {
          strongSelf.block(strongSelf);
        }
      });
    });
    [self resume];
  }
}

- (void)resume {
  if (!self.running || !self.suspended) {
    return;
  }
  dispatch_resume(self.gcdTimer);
  self.suspended = NO;
}

- (void)suspend {
  if (!self.running || self.suspended) {
    return;
  }

  dispatch_suspend(self.gcdTimer);
  self.suspended = YES;
}

- (void)stop {
  if (self.running) {
    [self resume];
    if (self.gcdTimer != nil) {
      dispatch_cancel(self.gcdTimer);
    }
  }
  self.suspended = YES;
  self.gcdTimer = nil;
}

- (void)dealloc {
  self.block = nil;
  [self stop];
}

@end
